package com.ruoyi.hiscomm.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.mapping.Property;
import co.elastic.clients.elasticsearch.indices.CreateIndexRequest;
import co.elastic.clients.elasticsearch.indices.PutMappingRequest;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.elasticsearch.model.EsPageResult;
import com.ruoyi.common.elasticsearch.service.ElasticSearchService;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.hiscomm.domain.DeptDictDo;
import com.ruoyi.hiscomm.mapper.DeptDictDoMapper;
import com.ruoyi.hiscomm.request.DeptParm;
import com.ruoyi.hiscomm.service.IDeptDictService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RList;
import org.redisson.api.RLock;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.ruoyi.common.core.utils.ListPaginator.getTotalPages;
import static com.ruoyi.common.core.utils.ListPaginator.paginate;

/**
 * 科室字典服务实现类
 *
 * @author samula
 * @date 2025-09-27
 */
@Service
@Slf4j
public class DeptDictServiceImpl extends ServiceImpl<DeptDictDoMapper, DeptDictDo> implements IDeptDictService {
    /**
     * 缓存记录
     */
    private static final String INDEX_NAME = "his_dict:dept_dict";
    /**
     * Es索引必须全部是小写
     */
    private static final String ES_INDEX_NAME = "dept_dict";
    /**
     * 缓存锁前缀
     */
    private static final String CACHE_LOCK_PREFIX = "his_dict:lock:";
    /**
     * 同步锁前缀
     */
    private static final String SYNC_LOCK_PREFIX = "his_dict:sync:";
    /**
     * 缓存过期时间(小时)
     */
    private static final long CACHE_EXPIRE_HOURS = 24;
    /**
     * 锁等待时间(秒)
     */
    private static final long LOCK_WAIT_SECONDS = 5;
    /**
     * 锁持有时间(秒)
     */
    private static final long LOCK_HOLD_SECONDS = 10;
    /**
     * 同步锁持有时间(秒)
     */
    private static final long SYNC_LOCK_HOLD_SECONDS = 30;
    /**
     * 缓存等待重试次数
     */
    private static final int CACHE_WAIT_RETRIES = 30;
    /**
     * 缓存等待间隔(毫秒)
     */
    private static final long CACHE_WAIT_INTERVAL_MS = 100;

    @Resource
    private ElasticSearchService elasticSearchService;
    @Resource
    private RedisService redisService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private ElasticsearchClient esClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateDeptDict(DeptDictDo deptDictDo) throws IOException {
        // 参数校验
        if (deptDictDo == null) {
            log.error("科室不能为空，更新失败");
            return 0;
        }

        if (StringUtils.isEmpty(deptDictDo.getDeptName()) || StringUtils.isEmpty(deptDictDo.getDeptCode()) || StringUtils.isEmpty(deptDictDo.getHospitalId())){
            log.error("科室要素[科室名称、科室代码、医院标识]不得为空，更新失败");
            return 0;
        }

        // 确保索引和映射存在
        initIndexAndMapping();

        // 1. 先更新数据库
        int updateCount = baseMapper.updateDeptDict(deptDictDo);
        if (updateCount > 0) {
            deptDictDo.generateId();
            // 2. 再更新到Elasticsearch
            elasticSearchService.save(ES_INDEX_NAME, deptDictDo.getId(), deptDictDo);

            // 3. 直接同步更新Redis缓存 (核心改动)
            syncDeptToRedis(deptDictDo);

            log.info("科室 [{}] 已同步更新到数据库、Elasticsearch和Redis", deptDictDo.getDeptName());
        }
        return updateCount;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addDeptDict(DeptDictDo dept) throws IOException {
        // 参数校验
        if (dept == null || StringUtils.isEmpty(dept.getDeptName())) {
            log.error("科室名称不能为空，添加失败");
            return 0;
        }

        // 检查科室名称唯一性
        if (checkDeptNameUnique(dept)) {
            log.warn("科室名称 [{}] 已存在，添加失败", dept.getDeptName());
            return 0;
        }

        // 确保索引和映射存在
        initIndexAndMapping();

        // 生成ID和创建时间
        dept.generateId();
        dept.setCreateDate(new Date());

        // 1. 先保存到数据库
        int insertCount = baseMapper.insertDeptDict(dept);
        if (insertCount > 0) {
            // 2. 再保存到Elasticsearch
            elasticSearchService.save(ES_INDEX_NAME, dept.getId(), dept);

            // 3. 直接同步更新Redis缓存 (核心改动)
            syncDeptToRedis(dept);

            log.info("科室 [{}] 已成功添加到数据库、Elasticsearch和Redis", dept.getDeptName());
        }
        return insertCount;
    }
    /**
     * 精准查询 (在deptName和inputCode字段上)
     */
    @Override
    public List<DeptDictDo> searchDeptDict(DeptDictDo dept) throws IOException {
        initIndexAndMapping();
        if (dept == null || StringUtils.isEmpty(dept.getDeptName())) {
            return Collections.emptyList();
        }
        List<String> searchFields = Arrays.asList("deptName", "inputCode");
        return elasticSearchService.multiSearch(ES_INDEX_NAME, searchFields, dept.getDeptName(), DeptDictDo.class);
    }

    /**
     * 模糊查询 (在deptName和deptAlias字段上)
     */
    @Override
    public List<DeptDictDo> searchDeptsWithFuzzy(DeptDictDo dept) throws IOException {
        initIndexAndMapping();
        if (dept == null) {
            return Collections.emptyList();
        }

        String deptName = StringUtils.trimAll(dept.getDeptName());
        if (StringUtils.isEmpty(deptName) || deptName.length() < 2) {
            return Collections.emptyList();
        }

        List<String> searchFields = Arrays.asList("deptName", "deptAlias");
        return elasticSearchService.fuzzyMultiSearch(ES_INDEX_NAME, searchFields, deptName, DeptDictDo.class);
    }

    /**
     * 检查科室名称是否唯一
     */
    @Override
    public boolean checkDeptNameUnique(DeptDictDo dept) throws IOException {
        if (dept == null || StringUtils.isEmpty(dept.getDeptName())) {
            return false; // 参数为空，不认为重复
        }
        // 从数据库中检查
        List<DeptDictDo> deptsFromDb = baseMapper.selectDepts(dept);
        if (deptsFromDb == null || deptsFromDb.isEmpty()) {
            return false;
        }

        // 如果是更新操作，需要排除自身
        if (!StringUtils.isEmpty(dept.getId())) {
            return deptsFromDb.stream().anyMatch(d -> !d.getId().equals(dept.getId()));
        }

        return true;
    }

    /**
     * 初始化同步科室数据
     */
    @PostConstruct
    public void ini() throws IOException {
        // 获取全局同步锁
        RLock syncLock = redissonClient.getLock(SYNC_LOCK_PREFIX + INDEX_NAME);
        boolean isSyncLocked = false;

        try {
            // 尝试获取锁
            isSyncLocked = syncLock.tryLock(LOCK_WAIT_SECONDS, SYNC_LOCK_HOLD_SECONDS, TimeUnit.SECONDS);
            if (!isSyncLocked) {
                log.info("已有线程正在执行 {} 同步操作，当前线程退出", INDEX_NAME);
                return;
            }

            // 1. 清理相关缓存和索引
            elasticSearchService.deleteIndex(ES_INDEX_NAME);
            clearDeptCache();

            // 2. 初始化索引和映射
            initIndexAndMapping();
            log.info("开始从数据库同步科室数据到Elasticsearch...");

            // 3. 从数据库查询数据
            List<DeptDictDo> deptList = listDepts();
            if (deptList == null || deptList.isEmpty()) {
                log.info("数据库中没有科室数据，同步结束。");
                return;
            }
            // 4. 同步数据到Elasticsearch
            Map<String, DeptDictDo> deptMap = new HashMap<>(deptList.size());
            for (DeptDictDo dept : deptList) {
                if (StringUtils.isEmpty(dept.getId())) {
                    dept.generateId();
                }
                deptMap.put(dept.getId(), dept); // key为文档ID，value为数据对象
            }
            elasticSearchService.bulkSave(ES_INDEX_NAME, deptMap); // 调用批量保存方法

            // 5. 重新加载缓存
            loadDeptCache(deptList);

            log.info("科室数据同步完成，共 [{}] 条。", deptList.size());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("同步过程被中断", e);
        } finally {
            // 确保锁释放
            releaseLock(syncLock, isSyncLocked);
        }
    }

    /**
     * 使用Elasticsearch进行分页模糊查询
     */
    @Override
    public IPage<DeptDictDo> pageESDeptList(DeptParm dept) {
        try {
            initIndexAndMapping();

            long current = ObjectUtils.isEmpty(dept.getCurrentPage()) ? 1 : dept.getCurrentPage();
            long size = ObjectUtils.isEmpty(dept.getPageSize()) ? 10 : dept.getPageSize();

            // 无搜索词时返回空分页
            if (StringUtils.isEmpty(dept.getDeptName())) {
                return new Page<>(current, size, 0);
            }

            // 使用ES进行分页模糊查询
            List<String> searchFields = Arrays.asList("deptName", "deptAlias", "deptCode");
            EsPageResult<DeptDictDo> esResult = elasticSearchService.fuzzyMultiSearchWithTotal(
                    ES_INDEX_NAME, searchFields, dept.getDeptName(), (int) current, (int) size, DeptDictDo.class);

            // 构建分页结果
            IPage<DeptDictDo> resultPage = new Page<>(current, size);
            resultPage.setRecords(esResult.getRecords());
            resultPage.setTotal(esResult.getTotal()); // TODO: 需改造ES服务层支持总数查询

            return resultPage;

        } catch (IOException e) {
            log.error("ES分页查询失败", e);
            return new Page<>();
        }
    }

    @Override
    public List<DeptDictDo> listFuzzyDepts(DeptParm dept) throws IOException {
        initIndexAndMapping();
        if (dept == null) {
            return Collections.emptyList();
        }

        String deptName = dept.getDeptName();
        if (StringUtils.isEmpty(deptName) || deptName.length() < 2) {
            return Collections.emptyList();
        }

        List<String> searchFields = Arrays.asList("deptName", "deptAlias");
        return elasticSearchService.fuzzyMultiSearch(ES_INDEX_NAME, searchFields, deptName, DeptDictDo.class);
    }

    @Override
    public IPage<DeptDictDo> pageRedisDeptList(DeptParm dept) {
        List<DeptDictDo> allDepts = listDepts();
        if (dept == null) {
            dept = new DeptParm();
        }
        // 处理分页参数默认值（已确保非null）
        int currentPage = dept.getCurrentPage() == null || dept.getCurrentPage() < 1
                ? 1
                : dept.getCurrentPage();
        int pageSize = dept.getPageSize() == null || dept.getPageSize() < 1
                ? 10
                : dept.getPageSize();
        // 限制最大页大小，避免一次性查询过多数据
        pageSize = Math.min(pageSize, 100);
        IPage<DeptDictDo> resultPage = new Page<>(currentPage, pageSize);
        String deptName = dept.getDeptName();
        if (StringUtils.isEmpty(deptName)) {
            allDepts = allDepts == null ? new ArrayList<>() : allDepts; // 防止null
            int total = allDepts.size();
            resultPage.setTotal(total);
            resultPage.setPages(getTotalPages(allDepts, pageSize)); // 使用处理后的pageSize
            resultPage.setRecords(paginate(allDepts, currentPage, pageSize)); // 使用处理后的参数
        } else {
            allDepts = allDepts.stream()
                    .filter(item -> item.getDeptName().contains(deptName))
                    .collect(Collectors.toList());
            allDepts = allDepts == null ? new ArrayList<>() : allDepts; // 防止null
            int total = allDepts.size();
            resultPage.setTotal(total);
            resultPage.setPages(getTotalPages(allDepts, pageSize)); // 使用处理后的pageSize
            resultPage.setRecords(paginate(allDepts, currentPage, pageSize)); // 使用处理后的参数
        }
        return resultPage;
    }

    @Override
    public boolean deleteDeptDict(String id) throws IOException {
        if (StringUtils.isEmpty(id)) {
            log.error("删除科室ID不能为空");
            return false;
        }

        // 1. 先从数据库删除
        // 解析id
        DeptDictDo deptFromId = createDeptFromId(id);
        if (ObjectUtils.isEmpty(deptFromId)){
            log.error("删除科室信息无效");
            return false;
        }

        int deleteCount = baseMapper.deleteDeptDictById(deptFromId);
        if (deleteCount > 0) {
            // 2. 再从Elasticsearch删除
            elasticSearchService.delete(ES_INDEX_NAME, id);

            // 3. 直接从Redis缓存中删除 (保持一致性)
            removeDeptFromRedis(id);

            log.info("科室 [ID: {}] 已成功从数据库、Elasticsearch和Redis删除", id);
            return true;
        }
        return false;
    }

    /**
     * 初始化索引和映射
     */
    public void initIndexAndMapping() throws IOException {
        try {
            // 检查索引是否存在
            if (elasticSearchService.existsIndex(ES_INDEX_NAME)) {
                log.debug("Elasticsearch索引 [{}] 已存在，无需初始化。", ES_INDEX_NAME);
                return;
            }

            log.info("Elasticsearch索引 [{}] 不存在，开始创建...", ES_INDEX_NAME);
            // 创建索引
            esClient.indices().create(CreateIndexRequest.of(c -> c.index(ES_INDEX_NAME)));
            log.info("索引 [{}] 创建成功。", ES_INDEX_NAME);

            // 短暂延时，确保索引就绪
            TimeUnit.MILLISECONDS.sleep(100);

            // 设置映射
            esClient.indices().putMapping(PutMappingRequest.of(p -> p
                    .index(ES_INDEX_NAME)
                    .properties(generateMap())
            ));
            log.info("Elasticsearch索引 [{}] 映射设置完成。", ES_INDEX_NAME);

        } catch (Exception e) {
            log.error("Elasticsearch索引初始化失败: {}", e.getMessage(), e);
            // 清理可能的残留索引
            try {
                if (elasticSearchService.existsIndex(ES_INDEX_NAME)) {
                    elasticSearchService.deleteIndex(ES_INDEX_NAME);
                    log.warn("已删除创建失败的索引 [{}]。", ES_INDEX_NAME);
                }
            } catch (Exception deleteEx) {
                log.error("清理失败索引时出错: {}", deleteEx.getMessage(), deleteEx);
            }
            throw new IOException("索引初始化失败，请检查ES服务和网络连接。", e);
        }
    }

    /**
     * 生成索引的映射关系
     */
    private Map<String, Property> generateMap() {
        Map<String, Property> properties = new HashMap<>(6);
        // deptCode 字段 (精确匹配)
        properties.put("deptCode", Property.of(p -> p.keyword(k -> k)));
        // deptName 字段 (中文分词)
        properties.put("deptName", Property.of(p -> p.text(t -> t.analyzer("ik_max_word").searchAnalyzer("ik_smart"))));
        // deptAlias 字段 (中文分词)
        properties.put("deptAlias", Property.of(p -> p.text(t -> t.analyzer("ik_max_word").searchAnalyzer("ik_smart"))));
        // inputCode 字段 (精确匹配)
        properties.put("inputCode", Property.of(p -> p.keyword(k -> k)));
        // hospitalId 字段 (精确匹配)
        properties.put("hospitalId", Property.of(p -> p.keyword(k -> k)));
        // serialNo 字段 (排序)
        properties.put("serialNo", Property.of(p -> p.long_(l -> l)));
        return properties;
    }

    /**
     * 获取所有科室列表（带缓存逻辑）
     */
    @Override
    public List<DeptDictDo> listDepts() {
        RList<DeptDictDo> rList = redissonClient.getList(INDEX_NAME);
        // 缓存已存在直接返回
        if (rList.isExists()) {
            log.info("缓存 {} 命中，直接读取", INDEX_NAME);
            return new ArrayList<>(rList);
        }
        // 缓存不存在，执行加载逻辑
        return loadDeptCacheIfAbsent();
    }

    /**
     * 缓存不存在时加载缓存
     */
    private List<DeptDictDo> loadDeptCacheIfAbsent() {
        RList<DeptDictDo> rList = redissonClient.getList(INDEX_NAME);
        RLock cacheLock = redissonClient.getLock(CACHE_LOCK_PREFIX + INDEX_NAME);
        boolean isCacheLocked = false;
        List<DeptDictDo> deptList = new ArrayList<>();

        try {
            log.info("缓存 {} 不存在，准备加载", INDEX_NAME);
            // 预查数据库，减少锁持有时间
            deptList = queryDeptFromDb();

            // 尝试获取分布式锁
            isCacheLocked = cacheLock.tryLock(LOCK_WAIT_SECONDS, LOCK_HOLD_SECONDS, TimeUnit.SECONDS);
            if (!isCacheLocked) {
                log.warn("获取锁失败，等待其他线程加载缓存");
                // 等待缓存加载完成
                return waitForCacheLoad(rList);
            }

            // 双重检查缓存是否存在
            if (rList.isExists()) {
                log.info("双重检查发现缓存已存在，直接读取");
                return new ArrayList<>(rList);
            }

            // 写入缓存并设置过期时间
            log.info("获取锁成功，开始写入缓存");
            rList.addAll(deptList);
            rList.expire(CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
            return deptList;

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("缓存加载过程被中断", e);
            return deptList;
        } finally {
            // 确保锁释放
            releaseLock(cacheLock, isCacheLocked);
        }
    }

    /**
     * 主动加载缓存
     */
    private void loadDeptCache(List<DeptDictDo> deptList) {
        RList<DeptDictDo> rList = redissonClient.getList(INDEX_NAME);
        RLock cacheLock = redissonClient.getLock(CACHE_LOCK_PREFIX + INDEX_NAME);
        boolean isCacheLocked = false;

        try {
            isCacheLocked = cacheLock.tryLock(LOCK_WAIT_SECONDS, LOCK_HOLD_SECONDS, TimeUnit.SECONDS);
            if (isCacheLocked) {
                rList.clear();
                rList.addAll(deptList);
                rList.expire(CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
                log.info("缓存 {} 重新加载完成，共 {} 条数据", INDEX_NAME, deptList.size());
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("加载缓存时被中断", e);
        } finally {
            releaseLock(cacheLock, isCacheLocked);
        }
    }

    /**
     * 等待缓存加载完成
     */
    private List<DeptDictDo> waitForCacheLoad(RList<DeptDictDo> rList) throws InterruptedException {
        int waitCount = 0;
        while (waitCount < CACHE_WAIT_RETRIES && !rList.isExists()) {
            TimeUnit.MILLISECONDS.sleep(CACHE_WAIT_INTERVAL_MS);
            waitCount++;
        }
        return rList.isExists() ? new ArrayList<>(rList) : queryDeptFromDb();
    }

    /**
     * 清除科室缓存
     */
    private void clearDeptCache() {
        try {
            redisService.deleteObject(INDEX_NAME);
            log.debug("科室缓存 {} 已清除", INDEX_NAME);
        } catch (Exception e) {
            log.warn("清除科室缓存失败", e);
        }
    }
    /**
     * 数据库查询科室数据
     */
    private List<DeptDictDo> queryDeptFromDb() {
        return baseMapper.selectDepts(new DeptDictDo());
    }

    /**
     * 安全释放锁
     */
    private void releaseLock(RLock lock, boolean isLocked) {
        if (isLocked && lock.isHeldByCurrentThread()) {
            try {
                lock.unlock();
                log.debug("锁 [{}] 已释放", lock.getName());
            } catch (IllegalMonitorStateException e) {
                log.warn("释放锁 [{}] 失败，可能已过期", lock.getName(), e);
            }
        }
    }

    // ========== Redis 缓存操作方法 (核心重构) ==========

    /**
     * 同步单个科室数据到Redis缓存（支持新增和更新）
     * 此方法必须在数据库事务成功提交后调用。
     *
     * @param dept 科室对象
     */
    private void syncDeptToRedis(DeptDictDo dept) {
        if (dept == null || StringUtils.isEmpty(dept.getId())) {
            log.error("科室ID不能为空，同步Redis失败");
            return;
        }

        // 使用科室ID作为锁键，粒度更细，并发性能更好
        RLock cacheLock = redissonClient.getLock(CACHE_LOCK_PREFIX + dept.getId());
        boolean isLocked = false;

        try {
            // 尝试获取锁
            isLocked = cacheLock.tryLock(LOCK_WAIT_SECONDS, LOCK_HOLD_SECONDS, TimeUnit.SECONDS);
            if (!isLocked) {
                log.warn("获取缓存锁失败，科室 [ID: {}] 的Redis同步可能延迟，将由下次查询触发更新。", dept.getId());
                return;
            }

            // 1. 更新哈希缓存 (RMap) - 高效，原子性覆盖
            String hashKey = "his_dict:dept_hash";
            RMap<String, DeptDictDo> deptHash = redissonClient.getMap(hashKey);
            deptHash.put(dept.getId(), dept);
            deptHash.expire(CACHE_EXPIRE_HOURS, TimeUnit.HOURS);

            // 2. 更新列表缓存 (RList) - 处理新增和更新
            RList<DeptDictDo> deptList = redissonClient.getList(INDEX_NAME);
            if (deptList.isExists()) {
                boolean replaced = false;
                for (int i = 0; i < deptList.size(); i++) {
                    DeptDictDo existingDept = deptList.get(i);
                    if (dept.getId().equals(existingDept.getId())) {
                        deptList.set(i, dept); // 更新
                        replaced = true;
                        break;
                    }
                }
                if (!replaced) {
                    deptList.add(dept); // 新增
                }
                deptList.expire(CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
            } else {
                // 如果列表缓存不存在，说明缓存可能已过期或被清除。
                // 此时最安全的做法是不操作列表，让下一次查询来触发完整的缓存加载。
                log.info("列表缓存 {} 不存在，仅更新哈希缓存。", INDEX_NAME);
            }

            log.info("科室 [ID: {}] 已成功同步到Redis缓存", dept.getId());

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("同步Redis缓存时被中断", e);
        } finally {
            releaseLock(cacheLock, isLocked);
        }
    }

    /**
     * 从Redis缓存中移除单个科室数据
     * @param id 科室ID
     */
    private void removeDeptFromRedis(String id) {
        if (StringUtils.isEmpty(id)) {
            return;
        }

        RLock cacheLock = redissonClient.getLock(CACHE_LOCK_PREFIX + id);
        boolean isLocked = false;

        try {
            isLocked = cacheLock.tryLock(LOCK_WAIT_SECONDS, LOCK_HOLD_SECONDS, TimeUnit.SECONDS);
            if (isLocked) {
                // 1. 从哈希中删除
                String hashKey = "his_dict:dept_hash";
                RMap<String, DeptDictDo> deptHash = redissonClient.getMap(hashKey);
                deptHash.remove(id);

                // 2. 从列表中删除
                RList<DeptDictDo> deptList = redissonClient.getList(INDEX_NAME);
                if (deptList.isExists()) {
                    // 先获取所有元素，找到匹配的ID
                    List<DeptDictDo> allDepts = new ArrayList<>(deptList);
                    boolean removed = false;

                    for (DeptDictDo dept : allDepts) {
                        if (id.equals(dept.getId())) {
                            // 使用Redisson的remove方法删除对象
                            deptList.remove(dept);
                            removed = true;
                            break;
                        }
                    }
                    if (!removed) {
                        log.debug("ID为 [{}] 的科室不在Redis列表缓存中", id);
                    }
                }

                log.info("科室 [ID: {}] 已从Redis缓存中移除", id);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("从Redis缓存移除科室时被中断", e);
        } finally {
            releaseLock(cacheLock, isLocked);
        }
    }

    /**
     * 从Redis获取单个科室数据
     * @param id 科室ID
     * @return DeptDictDo 或 null
     */
    public DeptDictDo getDeptFromRedis(String id) {
        if (StringUtils.isEmpty(id)) {
            return null;
        }
        String hashKey = "his_dict:dept_hash";
        RMap<String, DeptDictDo> deptHash = redissonClient.getMap(hashKey);
        return deptHash.get(id);
    }

    /**
     * 根据复合ID创建一个DeptDictDo对象，并填充id, hospitalId和deptCode字段。
     * 其他字段将保持为null。
     *
     * @param deptId 完整的科室ID
     * @return 一个部分填充的DeptDictDo对象，如果ID格式不正确则返回null。
     */
    private DeptDictDo createDeptFromId(String deptId) {
        if (StringUtils.isEmpty(deptId)) {
            log.error("科室ID为空，无法解析。");
            return null;
        }

        String[] parts = deptId.split("\\|\\|", 2);

        if (parts.length != 2 || StringUtils.isEmpty(parts[0]) || StringUtils.isEmpty(parts[1])) {
            log.error("科室ID格式不正确: {}", deptId);
            return null;
        }

        DeptDictDo deptShell = new DeptDictDo();
        deptShell.setId(deptId);
        deptShell.setHospitalId(parts[0]);
        deptShell.setDeptCode(parts[1]);

        return deptShell;
    }
}